package com.dream21th.common.utils;

import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import java.io.ByteArrayOutputStream;
import java.io.Serializable;
import java.net.URLDecoder;
import java.security.*;
import java.security.interfaces.RSAKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.Objects;

public class RSAUtils2 {
    /**
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
      /*  SimpleKeyPair simpleKeyPair = KeyGenerator.generateKey();
        System.out.println(simpleKeyPair.getPublicKeyString());
        System.out.println(simpleKeyPair.getPrivateKeyString());*/
        String params = "ZysSccrQkMH1sVu9M1w9lJB/QGGfWvznLj7zOCzEi+areWglXPgMdr9JtYFOOi4vjii4TWj3Zbq19wB3mrvr1RrDeeA9M3gQC6msdN7MlWV3xSu04nkW5U/bPU6fC/yHImd9es52+cwklSR6hzMznT/pWusR0dw/cRW3WdDMr5s=";
        String key = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAIJvPqqt9abj5c82Ap6LRq9Q27GIy+QXSbKRETNuVAeTN6HQOiMeqY41Uot/4rpgTGqH7u3qP7xCv+DmvX6NQGh7TPTOD33AbT9qWrpomFkOPLKZmdznz5FSMKQxnjoVZgDaRoz8ptFcROw3faIcGYRNBp8oQPZklqkd9ckpc8N3AgMBAAECgYB0qoI38g9mbjOwqpOfMd5Qk5dd7YjJhJbztFPZV9arS9+XyYMmxJdOTp19jy7CG75eDyYEAeFuR5oO1PQrjLZNLushhIFKMOhdGASXM53T7QShDD7tK04xWm/dBoDP0wjyLdcftWipbhUwyMOYyj0jYVpHqCundJUjUkXAbNQEaQJBAORTWcOY6xwYQcbe2BEtEjQ1pgvJSKYZrzS+CfyZ0nKltp9UfmPvyM4wKVHhwhUlS5pLY1bhRn/pVPpFDY8VfCMCQQCSPnYMTFkL78kfHa27xKGg7tLwMKjlHahlQ9u+bDLCL+fREg/Mr+3AFkb9yLFvNBAJm/krFUfW9iXEUKoxX/adAkAfawC9E3u2n/fdH6jnpCYPHRq+IHJouLwNK+E/6wK0V+0rO3sw0Tv2c1YcOE52jm06zAjdWZ5T142j6oCVIcW1AkEAiZX8B5v4lrNUG1MXtTTL/xVRHKoa69sYJ5VM8nzm0Cyzws1kUeUeH36V8YNT81Qe1nuIUxElUkanx2dAh01KPQJAb3dGnwLTCOv8PwKeW4gmF2jL2OGdbCCB9/IsMrr1CCItITLEpbrB/5dGDJsATbDPV6BhmSTKYimNxpuw3za0Ww==";
        String data = decrytDataString(params, key);
        System.out.println(data);


        String publicKey="MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCfrXenBMyWZ0i2Lmg7ytz0R/0zhMuVCOPP+VJrCck4zJnBbRYKqDWPwIOSDS5ZFnbNdhO+LZuAcrGypd0vL9PF6aqEZJ/ljcGhrbnySgIs4huRHE0Xlmhi1GF+ZPvTrOZUSljBFeSRNbwbY/ngXGSHU7XlN1FqY5VdubknHhlAVwIDAQAB";
        String data1=encryptDataString("123456",publicKey);
        System.out.println(data1);
        //String data2 = decrytDataString(data1, key);

       // System.out.println(data2);
    }

    /**
     * 解密数据
     *
     * @param params 请求返回的参数
     * @param key    rsa私钥
     * @return
     * @throws Exception
     */
    public static String decrytData(String params, String key) throws Exception {
        // 1.decode
        params = URLDecoder.decode(params, "utf-8");
        // 2.base64
        byte[] data = Base64.getDecoder().decode(params);
        // 3.rsa解密
        data = Helper.decrypt(data, key);
        return new String(data);
    }

    public static String decrytDataString(String str, String key) throws Exception {
        byte[] params = Base64.getDecoder().decode(str);

        byte[] data = Helper.decrypt(params, key);
        return new String(data);
    }

    public static String encryptDataString(String str, String key) throws Exception {
        byte[] params = Base64.getDecoder().decode(str);
        byte[] data = Helper.encrypt(params, key);
        return new String(data);
    }

    /**
     * 加解密辅助类
     */
    private static class Helper {

        private static final String ALGORITHM = "RSA";
        private static final KeyFactory keyFactory;

        static {
            try {
                keyFactory = KeyFactory.getInstance(ALGORITHM);
            } catch (NoSuchAlgorithmException e) {
                throw new RuntimeException("No such algorithm '" + ALGORITHM + "'", e);
            }
        }

        private static byte[] encrypt(byte[] data, String publicKey) throws Exception {
            if (Objects.isNull(data)) {
                return data;
            }

            PublicKey key = toPublicKey(publicKey);

            Cipher cipher = getCipher(Cipher.ENCRYPT_MODE, key);

            return doFinalInBlock(cipher, getBlockSize((RSAKey) key, true), data);
        }

        private static byte[] decrypt(byte[] data, String privateKey) throws Exception {
            if (Objects.isNull(data)) {
                return data;
            }

            PrivateKey key = toPrivateKey(privateKey);

            Cipher cipher = getCipher(Cipher.DECRYPT_MODE, key);

            return doFinalInBlock(cipher, getBlockSize((RSAKey) key, false), data);
        }

        private static int getBlockSize(RSAKey key, boolean encrypt) {
            int size = key.getModulus().bitLength() / 8;
            return encrypt ? size - 11 : size;
        }

        private static byte[] doFinalInBlock(Cipher cipher, int blockSize, byte[] data) throws Exception {
            int length = data.length;
            if (blockSize <= 0 || length <= blockSize) {
                return cipher.doFinal(data);
            }

            try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
                int offset = 0;
                for (; offset + blockSize <= length; offset += blockSize) {
                    byte[] bytes = cipher.doFinal(data, offset, blockSize);
                    out.write(bytes);
                }

                if (offset < length) {
                    out.write(cipher.doFinal(data, offset, length - offset));
                }

                out.flush();

                return out.toByteArray();
            }
        }

        private static Cipher getCipher(int mode, Key key)
                throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException {
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            cipher.init(mode, key);
            return cipher;
        }

        private static PublicKey toPublicKey(String publicKey) throws InvalidKeySpecException {
            byte[] keyBytes = decode(publicKey);

            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);

            return keyFactory.generatePublic(keySpec);
        }

        private static PrivateKey toPrivateKey(String privateKey) throws InvalidKeySpecException {
            byte[] keyBytes = decode(privateKey);

            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);

            return keyFactory.generatePrivate(keySpec);
        }

        private static byte[] decode(String key) {
            return Base64.getMimeDecoder().decode(key);
        }
    }


    private static class KeyGenerator {

        private static final KeyPairGenerator keyGenerator;

        static {
            try {
                keyGenerator = KeyPairGenerator.getInstance(Helper.ALGORITHM);
                keyGenerator.initialize(1024);
            } catch (NoSuchAlgorithmException e) {
                throw new RuntimeException("No such algorithm '" + Helper.ALGORITHM + "'", e);
            }
        }

        public static SimpleKeyPair generateKey() {
            KeyPair keyPair = keyGenerator.generateKeyPair();
            byte[] privateKey = keyPair.getPrivate().getEncoded();
            byte[] publicKey = keyPair.getPublic().getEncoded();
            return new SimpleKeyPair(privateKey, publicKey);
        }
    }

    private static class SimpleKeyPair implements Serializable {

        private static final long serialVersionUID = -3699965134810358040L;
        private final byte[] privateKey;
        private final byte[] publicKey;

        SimpleKeyPair(byte[] privateKey, byte[] publicKey) {
            this.privateKey = privateKey.clone();
            this.publicKey = publicKey.clone();
        }

        public byte[] getPrivateKey() {
            return privateKey.clone();
        }

        public byte[] getPublicKey() {
            return publicKey.clone();
        }

        public String getPrivateKeyString() {
            return Base64.getEncoder().encodeToString(getPrivateKey());
        }

        public String getPublicKeyString() {
            return Base64.getEncoder().encodeToString(getPublicKey());
        }
    }
}
